/*
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * RSNodeBase.cpp
 *
 *  Created on: 2010/03/03
 *      Author: Alexandre Pires ( c.alexandre.pires@gmail.com )
 */

#include "RSNodeBase.h"
#include "RSTransformationElement.h"

namespace Realsphere {

    RSNodeBase::RSNodeBase ( RSNodeType rNodeType, const QString& rId, const QString& rName, const QString& rScopeId ) :
        RSUIDElement ( rId, RS_NODE_TAG )
    {

        SetName ( rName, false );
        SetScopeId ( rScopeId, false );

        mNodeType = rNodeType;
        mLookAt = NULL;
        mSkew = NULL;
    }

    RSNodeBase::~RSNodeBase ()
    {
        mChildNodes.clear ();
        mRotations.clear ();
        mTranslations.clear ();
        mScales.clear ();
        mMatrices.clear ();
        mTransformations.clear ();
        mLookAt = NULL;
        mSkew = NULL;
    }

    QString RSNodeBase::ScopeId () const
    {
        return mScopeId;
    }

    RSTransformationElement* RSNodeBase::GetTransformation ( const QString& rScopeId ) const
    {
        RSTransformationsIterator iterator = RSTransformationsIterator ( mTransformations );

        while ( iterator.hasNext () )
        {
            RSTransformationElement* transformation = iterator.next ();
            QString transformation_sid = transformation->ScopeId ();

            if ( transformation_sid == rScopeId )
                return transformation;
        }

        return NULL;
    }

    RSTransformationElement* RSNodeBase::GetTransformation ( unsigned int rIndex ) const
    {
        return mTransformations[rIndex];
    }

    int RSNodeBase::IndexOf ( RSTransformationElement* rTransformationElement )
    {
        return mTransformations.indexOf ( rTransformationElement );
    }

    unsigned int RSNodeBase::TransformationsSize ()
    {
        return mTransformations.size ();
    }

    RSTransformations RSNodeBase::Transformations () const
    {
        return mTransformations;
    }

    RSTransformations RSNodeBase::Transformations ( RSTransformationType rTransformationType ) const
    {
        RSTransformations transformations;
        RSTransformationsIterator iterator = RSTransformationsIterator ( mTransformations );

        while ( iterator.hasNext () )
        {
            RSTransformationElement* transformation = iterator.next ();

            if ( transformation->TransformationType () == rTransformationType )
                transformations.append ( transformation );
        }

        return transformations;
    }

    RSTransformationsIterator RSNodeBase::TransformationsIterator () const
    {
        return RSTransformationsIterator ( mTransformations );
    }

    RSTransformationsIterator RSNodeBase::TransformationsIterator ( RSTransformationType rTransformationType ) const
    {
        return RSTransformationsIterator ( Transformations ( rTransformationType ) );
    }

    RSNodeBase* RSNodeBase::GetChildNode ( const QString& rId ) const
    {
        RSNodesIterator iterator = RSNodesIterator ( mChildNodes );

        while ( iterator.hasNext () )
        {
            RSNodeBase* node = iterator.next ();
            QString node_id = node->Id ();

            if ( node_id == rId )
                return node;
        }

        return NULL;
    }

    RSNodeBase* RSNodeBase::GetChildNode ( unsigned int rIndex ) const
    {
        return mChildNodes[rIndex];
    }

    int RSNodeBase::IndexOf ( RSNodeBase* rNodeBase )
    {
        return mChildNodes.indexOf ( rNodeBase );
    }

    unsigned int RSNodeBase::ChildNodesSize ( bool rRecursive )
    {
        unsigned int size = mChildNodes.size ();

        if ( !rRecursive )
            return size;

        RSNodesIterator iterator = RSNodesIterator ( mChildNodes );

        while ( iterator.hasNext () )
        {
            RSNodeBase* node_base = iterator.next ();
            size += node_base->ChildNodesSize ( true );
        }

        return size;
    }

    RSNodes RSNodeBase::ChildNodes () const
    {
        return mChildNodes;
    }

    RSNodesIterator RSNodeBase::ChildNodesIterator () const
    {
        return RSNodesIterator ( mChildNodes );
    }

    bool RSNodeBase::IsRootElement () const
    {
        return false;
    }

    bool RSNodeBase::IsDirty () const
    {
        return mDirty;
    }

    RSNodeType RSNodeBase::NodeType () const
    {
        return mNodeType;
    }

    QMatrix4x4 RSNodeBase::AccumlatedLocalTransform ()
    {
        if ( IsDirty () )
            UpdateTransformations ();

        return mAccumlatedLocalTransform;
    }

    QMatrix4x4 RSNodeBase::AccumlatedWorldTransform ()
    {
        if ( IsDirty () )
            UpdateTransformations ();

        return mAccumlatedWorldTransform;
    }

    void RSNodeBase::SetScopeId ( const QString& rScopeId, bool rEmit )
    {
        mScopeId = rScopeId;

        if ( rEmit )
            emit ScopeIdChanged ( this, rScopeId );
    }

    void RSNodeBase::SetDirty ( bool rOnOff )
    {
        mDirty = rOnOff;
    }

    RSError RSNodeBase::AddTransformation ( RSTransformationElement* rTransformationElement )
    {
        RSError error = AddChildElement ( rTransformationElement );
        return error;
    }

    RSError RSNodeBase::RemoveTransformation ( RSTransformationElement* rTransformationElement )
    {
        RSError error = RemoveChildElement ( rTransformationElement );
        return error;
    }

    RSError RSNodeBase::RemoveTransformation ( const QString& rScopeId )
    {
        RSTransformationElement* transformation = GetTransformation ( rScopeId );
        RSError error = RemoveTransformation ( transformation );
        return error;
    }

    RSError RSNodeBase::RemoveTransformation ( unsigned int rIndex )
    {
        RSTransformationElement* transformation = GetTransformation ( rIndex );
        RSError error = RemoveTransformation ( transformation );
        return error;
    }

    RSError RSNodeBase::AddChildNode ( RSNodeBase* rNodeBase )
    {
        RSError error = AddChildElement ( rNodeBase );
        return error;
    }

    RSError RSNodeBase::RemoveChildNode ( RSNodeBase* rNodeBase )
    {
        RSError error = RemoveChildElement ( rNodeBase );
        return error;
    }

    RSError RSNodeBase::RemoveChildNode ( const QString& rId )
    {
        RSNodeBase* node_base = GetChildNode ( rId );
        RSError error = RemoveChildNode ( node_base );
        return error;
    }

    RSError RSNodeBase::RemoveChildNode ( unsigned int rIndex )
    {
        RSNodeBase* node_base = GetChildNode ( rIndex );
        RSError error = RemoveChildNode ( node_base );
        return error;
    }

    RSError RSNodeBase::BeforeAddChildElement ( RSElement* rChild )
    {
        RSError error = RSElement::BeforeAddChildElement ( rChild );

        if ( error == RS_INVALID_ELEMENT )
        {
            if ( rChild->InstanceOf ( RS_NODE_BASE_CLASS ) )
                error = RS_NO_ERROR;

            if ( rChild->InstanceOf ( RS_TRANSFORMATION_ELEMENT_CLASS ) )
            {
                RSTransformationElement* transformation = RSTransformationElementCast( rChild );

                if ( transformation->TransformationType () == RS_LOOK_AT )
                {
                    if ( mLookAt != NULL )
                        RemoveChildElement ( mLookAt );

                    mLookAt = NULL;
                    error = RS_NO_ERROR;
                }
                else if ( transformation->TransformationType () == RS_SKEW )
                {
                    if ( mSkew != NULL )
                        RemoveChildElement ( mSkew );

                    mSkew = NULL;
                    error = RS_NO_ERROR;
                }
                else if ( GetTransformation ( transformation->ScopeId () ) != NULL )
                {
                    error = RS_SID_ALREADY_TAKEN;
                }
                else
                {
                    error = RS_NO_ERROR;
                }
            }
        }

        return error;
    }

    bool RSNodeBase::AfterAddChildElement ( RSElement* rChild )
    {
        bool handled = RSElement::AfterAddChildElement ( rChild );

        if ( !handled )
        {
            if ( rChild->InstanceOf ( RS_NODE_BASE_CLASS ) )
            {
                RSNodeBase* node_base = RSNodeBaseCast ( rChild );
                mChildNodes.append ( node_base );
                handled = true;

                node_base->SetDirty ( true );

                emit NodeChildAdded ( this, node_base );
            }
            else if ( rChild->InstanceOf ( RS_TRANSFORMATION_ELEMENT_CLASS ) )
            {
                RSTransformationElement* transformation = RSTransformationElementCast ( rChild );
                mTransformations.append ( transformation );

                switch ( transformation->TransformationType () )
                {
                    case RS_LOOK_AT:
                        mLookAt = transformation;
                        break;
                    case RS_MATRIX:
                        mMatrices.append ( transformation );
                        break;
                    case RS_ROTATE:
                        mRotations.append ( transformation );
                        break;
                    case RS_SCALE:
                        mScales.append ( transformation );
                        break;
                    case RS_SKEW:
                        mSkew = transformation;
                        break;
                    case RS_TRANSLATE:
                        mTranslations.append ( transformation );
                        break;
                    default:
                        break;
                }

                handled = true;

                mDirty = true;

                emit TransformationAdded ( this, transformation );
            }
        }

        return handled;
    }

    bool RSNodeBase::AfterRemoveChildElement ( RSElement* rChild )
    {
        bool handled = RSElement::AfterRemoveChildElement ( rChild );

        if ( !handled )
        {
            if ( rChild->InstanceOf ( RS_NODE_BASE_CLASS ) )
            {
                RSNodeBase* node_base = RSNodeBaseCast ( rChild );
                int node_base_index = mChildNodes.indexOf ( node_base );
                mChildNodes.removeAt ( node_base_index );
                handled = true;

                node_base->SetDirty ( true );

                emit NodeChildRemoved ( this, node_base );
            }
            else if ( rChild->InstanceOf ( RS_TRANSFORMATION_ELEMENT_CLASS ) )
            {
                RSTransformationElement* transformation = RSTransformationElementCast ( rChild );
                int tansformation_index = mTransformations.indexOf ( transformation );
                mTransformations.removeAt ( tansformation_index );

                switch ( transformation->TransformationType () )
                {
                    case RS_LOOK_AT:
                        mLookAt = NULL;
                        break;
                    case RS_MATRIX:
                        tansformation_index = mMatrices.indexOf ( transformation );
                        mMatrices.removeAt ( tansformation_index );
                        break;
                    case RS_ROTATE:
                        tansformation_index = mRotations.indexOf ( transformation );
                        mRotations.removeAt ( tansformation_index );
                        break;
                    case RS_SCALE:
                        tansformation_index = mScales.indexOf ( transformation );
                        mScales.removeAt ( tansformation_index );
                        break;
                    case RS_SKEW:
                        mSkew = NULL;
                        break;
                    case RS_TRANSLATE:
                        tansformation_index = mTranslations.indexOf ( transformation );
                        mTranslations.removeAt ( tansformation_index );
                        break;
                    default:
                        break;
                }

                handled = true;

                mDirty = true;

                emit TransformationRemoved ( this, transformation );
            }
        }

        return handled;
    }

    void RSNodeBase::UpdateTransformations ()
    {

        RSNodeBase* parent_node = RSNodeBaseCast ( Parent() );
        RSTransformationElement* transformation;

        mAccumlatedLocalTransform.setToIdentity ();
        mAccumlatedWorldTransform.setToIdentity ();

        RSTransformationsIterator iterator = RSTransformationsIterator ( mMatrices );

        while ( iterator.hasNext () )
        {
            transformation = iterator.next ();
            mAccumlatedLocalTransform *= transformation->Transformation ();
        }

        iterator = RSTransformationsIterator ( mRotations );

        while ( iterator.hasNext () )
        {
            transformation = iterator.next ();
            mAccumlatedLocalTransform *= transformation->Transformation ();
        }

        iterator = RSTransformationsIterator ( mScales );

        while ( iterator.hasNext () )
        {
            transformation = iterator.next ();
            mAccumlatedLocalTransform *= transformation->Transformation ();
        }

        iterator = RSTransformationsIterator ( mTransformations );

        while ( iterator.hasNext () )
        {
            transformation = iterator.next ();
            mAccumlatedLocalTransform *= transformation->Transformation ();
        }

        if ( parent_node != NULL )
        {
            mAccumlatedWorldTransform *= parent_node->AccumlatedWorldTransform ();
        }

        mAccumlatedWorldTransform *= mAccumlatedLocalTransform;

        mDirty = false;
    }
}
